﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using PivotalTFSSync.Pivotal;
using PivotalTFSSync.Properties;
using PivotalTFSSync.TFS;

namespace PivotalTFSSync
{
	public partial class frmMain : Form
	{
		private const string PIVOTALID = "PIVOTALID:";
		private ITFS TFS;
		private IPivotal pivotal;

		public frmMain()
		{
			InitializeComponent();

			Settings.Default.SettingsLoaded += Settings_SettingsLoaded;
		}

		private void Settings_SettingsLoaded(object sender, SettingsLoadedEventArgs e)
		{
			if (IsPivotalLoginInformationPresent(true))
				ConnectToPivotalAndPopulateData();

			if (IsTFSLoginInformationPresent((true)))
			{
				ConnectToTFS();
				LoadTFSProjects();
			}
		}

		private void ConnectToTFS()
		{
			try
			{
				TFS = new TFS.TFS(txtTFSServerURL.Text, txtDomain.Text, txtTFSUsername.Text, txtTFSPassword.Text);
			}
			catch (Exception exception)
			{
				LogError(exception);
			}
		}

		private void btnGetStories_Click(object sender, EventArgs e)
		{
			var stories = GetPivotalStories();
			PopulateTreeview(stories);
		}

		private IEnumerable<Story> GetPivotalStories()
		{
			Cursor = Cursors.WaitCursor;

			try
			{
				var iterations = pivotal.GetIteration((Project) cboPivotalProjects.SelectedItem,
				                                      ((Pivotal.Pivotal.IterationVersion) cboPivotalIteration.SelectedItem));
				return iterations.First().Stories.stories;
			}
			catch (Exception ex)
			{
				LogError(ex);
			}
			finally
			{
				Cursor = Cursors.Arrow;
			}
			return new Story[] {};
		}

		private void LogError(Exception exception)
		{
			lstMessages.Items.Add(exception.Message);
		}

		private void PopulateTreeview(IEnumerable<Story> stories)
		{
			tvwDetails.Nodes.Clear();

			foreach (var story in stories)
			{
				var key = story.Id.ToString();
				var node = tvwDetails.Nodes.Add(key, story.Name, story.Type);
				node.Tag = story;

				if (story.Description.Trim().Length > 0)
					node.Nodes.Add(string.Concat(key, "-2"), story.Description.Trim(), "empty");
			}
		}

		private void frmMain_Load(object sender, EventArgs e)
		{
			IsReadyForSync();
		}

		private void PopulatePivotalProjectsCombo()
		{
			cboPivotalProjects.DataSource = pivotal.GetAllProjects();
			cboPivotalProjects.DisplayMember = "Name";
		}

		private void LoadTFSProjects()
		{
			try
			{
				var tfs = TFS;
				cboTFSProjects.DataSource = tfs.GetProjects();
				cboTFSProjects.DisplayMember = "Name";
			}
			catch (Exception exception)
			{
				LogError(exception);
			}
		}

		private void PopulatePivotalIterationsCombo()
		{
			cboPivotalIteration.Items.Clear();
			cboPivotalIteration.Items.Add(Pivotal.Pivotal.IterationVersion.All);
			cboPivotalIteration.Items.Add(Pivotal.Pivotal.IterationVersion.Backlog);
			cboPivotalIteration.Items.Add(Pivotal.Pivotal.IterationVersion.Current);
			cboPivotalIteration.Items.Add(Pivotal.Pivotal.IterationVersion.Done);
		}

		private void PopulateTFSIterationsCombo(string projectname)
		{
			try
			{
				var tfs = TFS;
				cboTFSIterations.DataSource = tfs.GetIterationPaths(projectname);
				cboTFSIterations.DisplayMember = "Name";
			}
			catch (Exception exception)
			{
				LogError(exception);
			}
		}

		private void btnSyncSelected_Click(object sender, EventArgs e)
		{
			SyncCheckedItems();
			PopulateTFSWorkItemList();
		}

		private void PopulateTFSWorkItemList()
		{
			tvwTFS.Nodes.Clear();
			var workitemTypes = TFS.GetWorkItemTypes(cboTFSProjects.Text);

			var backlogcollection = TFS.GetWorkItems(cboTFSProjects.Text, cboTFSIterations.Text, cboTFSSubPath.Text,
			                                         workitemTypes["Product Backlog Item"], -1);
			//var workitemcollection = GetTFSWorkItems(workitemTypes["Sprint Backlog Item"]);

			if (backlogcollection == null)
			{
				return;
			}

			foreach (WorkItem backlogItem in backlogcollection)
			{
				var key = backlogItem.Id.ToString();
				var node = tvwTFS.Nodes.Add(key, backlogItem.Title, backlogItem.Type.Name);
				node.Tag = backlogItem;

				//var workitemcollection = TFS.GetWorkItems(cboTFSProjects.Text, cboTFSIterations.Text, cboTFSSubPath.Text, workitemTypes["Sprint Backlog Item"], backlogItem.Id);

				//foreach (WorkItem workitem in workitemcollection)
				//{
				//    var workitemkey = workitem.Id.ToString();
				//    var workitemnode = tvwTFS.Nodes.Add(workitemkey, workitem.Title, workitem.Type.Name);
				//    workitemnode.Tag = workitem;

				//    if (workitem.Description.Trim().Length > 0)
				//    {
				//        workitemnode.Nodes.Add(string.Concat(workitemkey, "-2"), workitem.Description.Trim(), "empty");
				//    }
				//}
			}
		}

		private void SyncCheckedItems()
		{
			Cursor = Cursors.WaitCursor;
			var progress = new frmProgress();
			try
			{
				progress.Show(this);
				var currentPriority = int.Parse(txtStartPriority.Text);
				var priorityStep = int.Parse(txtPriorityStep.Text);
	 
				var projectname = ((Microsoft.TeamFoundation.WorkItemTracking.Client.Project) cboTFSProjects.SelectedItem).Name;

				var checkednodes = GetCheckedTreeNodes();

				var itemsprocessed = 1;
				foreach (var node in checkednodes)
				{
					progress.SetMessage(string.Format("Copying item {0} of {1}", itemsprocessed, checkednodes.Count()));
					var story = (Story) node.Tag;
					story.Priority = currentPriority;
					TFS.AddPivotalStoryToTFS(story, projectname, ((Node) cboTFSIterations.SelectedItem).Name,
					                         ((Node) cboTFSSubPath.SelectedItem).Name);
					currentPriority = currentPriority + priorityStep;
					itemsprocessed++;
				}
			}
			finally
			{
				Cursor = Cursors.Arrow;
				progress.Close();
			}
		}

		private IEnumerable<TreeNode> GetCheckedTreeNodes()
		{
			return tvwDetails.Nodes.Cast<TreeNode>().Where(node => node.Checked);
		}

		private void cboProjects_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (cboTFSProjects.SelectedItem != null)
			{
				PopulateTFSIterationsCombo(
					((Microsoft.TeamFoundation.WorkItemTracking.Client.Project) cboTFSProjects.SelectedItem).Name);
			}
		}

		private static bool IsChildNode(TreeNode node)
		{
			return node.Parent != null;
		}

		private static void tvwDetails_BeforeCheck(object sender, TreeViewCancelEventArgs e)
		{
			if (IsChildNode(e.Node))
			{
				e.Node.Parent.Checked = !e.Node.Parent.Checked;
				e.Cancel = true;
			}
		}

		private void cboDestinationIteration_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (cboTFSIterations.SelectedItem != null)
			{
				cboTFSSubPath.DataSource = ((Node) cboTFSIterations.SelectedItem).ChildNodes;
				cboTFSSubPath.DisplayMember = "Name";
			}
		}

		private void btnExport_Click(object sender, EventArgs e)
		{
			Cursor = Cursors.WaitCursor;
			try
			{
				IEnumerable<Iteration> iterations = pivotal.GetIteration((Project) cboPivotalProjects.SelectedItem,
				                                                         ((Pivotal.Pivotal.IterationVersion)
				                                                          cboPivotalIteration.SelectedItem));

				using (FileStream tempFileStream = File.OpenWrite(@"c:\stories.csv"))
				{
					var serializer = new StorySerializer
					                 	{
					                 		RowDelimiter = '\n',
					                 		ColumnDelimiter = '\t',
					                 		EncapsulateAllFieldsWith = '"'
					                 	};

					foreach (Iteration iteration in iterations)
					{
						WriteStoriesToStream(iteration, iteration.Stories.stories, serializer, tempFileStream);
					}

					if (((Pivotal.Pivotal.IterationVersion) cboPivotalIteration.SelectedItem) == Pivotal.Pivotal.IterationVersion.All)
					{
						//adding icebox stories.
						IEnumerable<Story> storiesInIceBox = pivotal.GetStoriesByFilter((Project) cboPivotalProjects.SelectedItem,
						                                                                "state:unscheduled");
						WriteStoriesToStream(null, storiesInIceBox, serializer, tempFileStream);
					}

					tempFileStream.Close();
				}
			}
			catch (Exception ex)
			{
				LogError(ex);
			}
			Cursor = Cursors.Arrow;
		}

		private static void WriteStoriesToStream(Iteration iteration, IEnumerable<Story> stories, StorySerializer serializer,
		                                         FileStream tempFileStream)
		{
			serializer.iteration = iteration;
			var stream = serializer.Serialize(stories);

			var buffer = new byte[4096];
			int bytesRead;

			while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
				tempFileStream.Write(buffer, 0, bytesRead);
		}

		private void btnSync_Click(object sender, EventArgs e)
		{
			if (numSyncInterval.Value <= 0) return;

			tmrSync.Interval = decimal.ToInt32(numSyncInterval.Value)*1000;
			ToggleSyncText();
			tmrSync.Enabled = !tmrSync.Enabled;
		}

		private void ToggleSyncText()
		{
			btnSync.Text = btnSync.Text == "Stop Sync" ? "Start Sync" : "Stop Sync";
		}

		private void tmrSync_Tick(object sender, EventArgs e)
		{
			SyncStatuses();
		}

		private void SyncStatuses()
		{
			var pivotalStoriesdictionary = GetPivotalStories().ToDictionary(d => d.Id);
			try
			{
				Monitor.Enter(pivotalStoriesdictionary);

				var workitemTypes = TFS.GetWorkItemTypes(cboTFSProjects.Text);
				var tfsWorkItems = TFS.GetWorkItems(cboTFSProjects.Text, cboTFSIterations.Text, cboTFSSubPath.Text,
				                                    workitemTypes["Product Backlog Item"], -1);

				switch (cboSyncDirection.SelectedIndex)
				{
					case 0:
						break;
					case 1:
						SyncTFSWorkItemStatusesToPivotal(pivotalStoriesdictionary, tfsWorkItems);
						break;
					default:
						SyncTFSWorkItemStatusesToPivotal(pivotalStoriesdictionary, tfsWorkItems);
						break;
				}
			}
			finally
			{
				Monitor.Exit(pivotalStoriesdictionary);
			}
		}

		private void SyncTFSWorkItemStatusesToPivotal(Dictionary<int, Story> pivotalStoriesdictionary,
		                                              WorkItemCollection tfsWorkItems)
		{
			foreach (WorkItem workItem in tfsWorkItems)
			{
				var pivotalId = GetPivotalIdFromTFSWorkItem(workItem);

				if (pivotalId <= -1) continue;

				if (!pivotalStoriesdictionary.ContainsKey(pivotalId)) continue;

				var pivotalstory = pivotalStoriesdictionary[pivotalId];

				if (pivotalstory.CurrentState == workItem.State) continue;

				pivotalstory.CurrentState = workItem.State;
				pivotal.SaveStory((Project) cboPivotalProjects.SelectedItem, pivotalstory);
			}
		}

		private static int GetPivotalIdFromTFSWorkItem(WorkItem item)
		{
			var historystring = (string) item.Fields["History"].Value;

			if (!string.IsNullOrEmpty(historystring) && historystring.Contains(PIVOTALID))
			{
				var pivotalid = historystring.Substring(historystring.IndexOf(PIVOTALID) + 1);
				int pivotalIdAsInt;
				if (int.TryParse(pivotalid, out pivotalIdAsInt))
					return pivotalIdAsInt;
			}

			return -1;
		}

		private void btnWizard_Click(object sender, EventArgs e)
		{
			Form frm = new frmWizard();
			frm.ShowDialog(this);
		}

		private void Validate_ReadyForSync(object sender, EventArgs e)
		{
			IsReadyForSync();
		}

		private bool IsReadyForSync()
		{
			bool isvalid = true;

			isvalid = IsPivotalLoginInformationPresent(isvalid);

			isvalid = IsTFSLoginInformationPresent(isvalid);

			isvalid = IsStepInformationPresent(isvalid);
			btnSyncSelected.Enabled = isvalid;
			picLeft.Enabled = isvalid;
			picRight.Enabled = isvalid;

			return isvalid;
		}

		private bool IsStepInformationPresent(bool isvalid)
		{
			if (txtPriorityStep.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid TFS Prioritystep.");
			}

			if (txtStartPriority.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid TFS Startpriority.");
			}
			return isvalid;
		}

		private bool IsTFSLoginInformationPresent(bool isvalid)
		{
			if (txtTFSUsername.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid TFS username.");
			}

			if (txtTFSPassword.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid TFS password.");
			}
			if (txtTFSServerURL.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid TFS ServerURL.");
			}
			if (txtDomain.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid TFS Domain.");
			}
			btnConnectToTFS.Enabled = isvalid;
			EnableTFSGroup(isvalid);
			return isvalid;
		}

		private void EnableTFSGroup(bool isvalid)
		{
			grpTFS.Enabled = isvalid;
		}

		private bool IsPivotalLoginInformationPresent(bool isvalid)
		{
			if (txtPivotalUsername.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid Pivotal username.");
			}

			if (txtPivotalPassword.Text.Trim().Length == 0)
			{
				isvalid = false;
				lstMessages.Items.Add("Invalid Pivotal password.");
			}
			//	btnConnectToTFS.Enabled = isvalid;

			btnGetStories.Enabled = isvalid;

			btnConnectToPivotal.Enabled = isvalid;
			return isvalid;
		}

		private void cboTFSSubPath_Enter(object sender, EventArgs e)
		{
			//retrieve next priority and the step size from tfs.
		}

		private void btnConnectToPivotal_Click(object sender, EventArgs e)
		{
			ConnectToPivotalAndPopulateData();
		}

		private void ConnectToPivotalAndPopulateData()
		{
			try
			{
				Cursor = Cursors.WaitCursor;
				if (VerifyLoginToPivotal())
				{
					PopulatePivotalProjectsCombo();
					PopulatePivotalIterationsCombo();
					btnGetStories.Enabled = true;
				}
			}
			catch (Exception exception)
			{
				LogError(exception);
			}
			Cursor = Cursors.Arrow;
		}

		private bool VerifyLoginToPivotal()
		{
			try
			{
				pivotal = new Pivotal.Pivotal(txtPivotalUsername.Text, txtPivotalPassword.Text);
				return true;
			}
			catch (Exception exception)
			{
				LogError(exception);
				return false;
			}
		}

		private void picRight_Click(object sender, EventArgs e)
		{
			SyncCheckedItems();
			PopulateTFSWorkItemList();
		}

		private void btnConnectToTFS_Click(object sender, EventArgs e)
		{
			ConnectToTFS();
			LoadTFSProjects();
		}

		private void cboTFSSubPath_DropDownClosed(object sender, EventArgs e)
		{
			var prioritystep = 10;
			var nextpriority = 10;
			try
			{
				TFS.GetNextPriorityNumberAndPriorityStep(cboTFSProjects.Text, cboTFSIterations.Text, cboTFSSubPath.Text,
				                                         out nextpriority, out prioritystep);
			}
			catch (Exception ex)
			{
				LogError(ex);
			}

			txtPriorityStep.Text = prioritystep.ToString();
			txtStartPriority.Text = nextpriority.ToString();
			PopulateTFSWorkItemList();
		}

		private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
		{
			Settings.Default.Save();
		}

		private void tmrErrorMessage_Tick(object sender, EventArgs e)
		{
			if (lstMessages.Items.Count > 0)
			{
				lstMessages.Items.RemoveAt(0);
			}
		}

		private void tvwTFS_DragDrop(object sender, DragEventArgs e)
		{
			//TODO: kopiera pivotal till tfs:en och stoppa in den imellan de droppade. 
			//Ta fram den task som ligger ovanför
			//Ta fram den task som ligger efter droppen.
			// Ta snittet om det finns ett och anv. detta som prioriterings nummer vid skapandet utav ny task. 
			//Om ingen diff finns (om den bara är 1 så ta den ovanför +1

			// Get the tree.
			var tree = (TreeView) sender;

			// Get the screen point.
			var pt = new Point(e.X, e.Y);

			// Convert to a point in the TreeView's coordinate system.
			pt = tree.PointToClient(pt);

			// Get the node underneath the mouse.
			var node = tree.GetNodeAt(pt);


			//add the dropped one after the node.
			var workitemBefore = (WorkItem) node.Tag;
			var workitemAfter= (WorkItem) node.NextNode.Tag;

			var priorityDiff =Math.Abs( (int) workitemAfter.Fields[TFSConstants.ConchangoTeamsystemScrumBusinesspriority].Value -
			                   (int) workitemBefore.Fields[TFSConstants.ConchangoTeamsystemScrumBusinesspriority].Value);

			if (priorityDiff<2)
			{
				priorityDiff = 2;
			}
			var newPriority =(int) workitemBefore.Fields[TFSConstants.ConchangoTeamsystemScrumBusinesspriority].Value +  priorityDiff/2;

			var pivotalBacklogItemToAdd = (Story)((TreeNode)e.Data).Tag;
			pivotalBacklogItemToAdd.Priority = newPriority;
			
			TFS.AddPivotalStoryToTFS(pivotalBacklogItemToAdd, cboTFSProjects.Text, cboTFSIterations.Text, cboTFSSubPath.Text);
			PopulateTFSWorkItemList();
		}

		private void tvwDetails_MouseDown(object sender, MouseEventArgs e)
		{
			// Get the tree.
			var tree = (TreeView) sender;

			// Get the node underneath the mouse.
			var node = tree.GetNodeAt(e.X, e.Y);
			tree.SelectedNode = node;

			// Start the drag-and-drop operation with a cloned copy of the node.
			if (node == null) return;

			tree.DoDragDrop(node.Clone(), DragDropEffects.Copy);
		}

		private void tvwTFS_DragOver(object sender, DragEventArgs e)
		{
			// Get the tree.
			var tree = (TreeView) sender;

			// Drag and drop denied by default.
			e.Effect = DragDropEffects.None;

			// Is it a valid format?
			if (e.Data.GetData(typeof (TreeNode)) == null) return;

			// Get the screen point.
			var pt = new Point(e.X, e.Y);

			// Convert to a point in the TreeView's coordinate system.
			pt = tree.PointToClient(pt);

			// Is the mouse over a valid node?
			var node = tree.GetNodeAt(pt);

			if (node == null) return;

			e.Effect = DragDropEffects.Copy;
			tree.SelectedNode = node;
		}
	}
}